/**
 * 200925：增加线性插值可选
 * 210325: 增加goto回调函数的tar和pos参数，以实现动态追及
 * 20220304:[require] to [import]...jordia
 * 20220321:引入THREE模块spherical修改为Spherical
 */
import {EventDispatcher,Vector3,Spherical} from 'three';
import Utils from '../general/utils';

const DEFAULT_SPEED=0.05;
const DEFAULT_LIMIT=0.01;

/**
 * 摄像机对齐（动画）
 */
export default class CameraTarget{
    constructor(camCtr){
        this.camCtr=camCtr;
        this.init();
    }

    //--------------------------------对外属性及方法--------------------------------

    /**
     * 设置相机控制对象
     * @param camCtr
     */
    set cameraController(camCtr){
        this.camCtr=camCtr;
    }

    /**
     * 设置相机运动速度
     * @param val {number}
     */
    set speed(val){
        this._camSpeed=val<=0?DEFAULT_SPEED:val;
    }

    /**
     * 是否线性插值
     * @param bol
     */
    set linear(bol){
        this._linear=bol;
    }

    /**
     * 设置极限阈值(非线性插值时有效)
     * @param val
     */
    set limit(val){
        this._minLimit=val<=0?DEFAULT_LIMIT:val;
    }

    /**
     * 获取事件对象（可用事件：{type:'going',paras:{value:bool}}）
     * @returns {EventDispatcher | null}
     */
    get event(){
        return this.eventCenter;
    }

    /**
     * 设置退出状态相机控制是否可用
     * @param bol
     */
    set originalEnabled(bol){
        this._oriCamEnabled=bol;
    }

    /**
     * 对齐到（若tar或pos参数为null，则退出对齐动作）
     * @param tar {Vector3|null} 相机焦点
     * @param pos {Vector3|null} 相机位置
     * @param callback {function} 回调函数（参数：当前进程值）
     */
    goTo(tar,pos,callback=(prg,tar,pos)=>{}){
        if(tar&&pos) {
            if(tar.equals(this.camCtr.target)&&pos.equals(this.camCtr.camera.position)){
                if(callback)callback(null);
                return;
            }
            cancelAnimationFrame(this.requests[0]);
            let startTheta = this.camCtr.spherical.theta;
            let startPhi = this.camCtr.spherical.phi;
            let startRadius = this.camCtr.spherical.radius;
            let startTarget = this._tV0.copy(this.camCtr.target);
            let spherical = this._tS0.setFromVector3(this._tV2.subVectors(pos, tar));
            let endTheta = spherical.theta;
            let endPhi = spherical.phi;
            let endRadius = tar.distanceTo(pos);
            let prg = 0;
            let dtaTheta=endTheta - startTheta;
            dtaTheta=Math.abs(dtaTheta)>Math.PI?Math.sign(dtaTheta)*(Math.abs(dtaTheta)-Math.PI*2):dtaTheta;
            let dtaPhi=endPhi - startPhi;
            let dtaRadius=endRadius - startRadius;
            this.fireCamGoEvent(true);
            this.camCtr.enabled = false;
            let ani = () => {
                this._linear?prg += this._camSpeed:prg += this._camSpeed * (Utils.clamp(1 - prg, this._minLimit, 1));
                prg=Utils.clamp(prg,0,1);
                let theta = startTheta + dtaTheta * prg;
                let phi = startPhi + dtaPhi * prg;
                let radius = startRadius + dtaRadius * prg;
                spherical.set(radius, phi, theta);
                let cent = this._tV1.subVectors(tar, startTarget).multiplyScalar(prg).add(startTarget);
                this.camCtr.target.copy(cent);
                this.camCtr.spherical = spherical.makeSafe();
                if(callback)callback(prg,tar,pos);
                if (prg >= 1) {
                    if(callback)callback(null);
                    cancelAnimationFrame(this.requests[0]);
                    this.requests[0]=null;
                    prg = 0;
                    this.camCtr.enabled = this._oriCamEnabled;
                    this.fireCamGoEvent(false);
                    return;
                }
                this.requests[0] = requestAnimationFrame(ani);
            };
            ani();
        }else{
            cancelAnimationFrame(this.requests[0]);
            this.requests[0]=null;
            this.camCtr.enabled = this._oriCamEnabled;
            this.fireCamGoEvent(false);
        }
    }

    /**
     * 立即对齐到
     * @param tar {Vector3} 相机焦点
     * @param pos {Vector3} 相机位置
     */
    setTo(tar,pos){
        if(this.requests[0]!==null){
            cancelAnimationFrame(this.requests[0]);
            this.requests[0]=null;
        }
        let spherical = this._tS0.setFromVector3(this._tV2.subVectors(pos, tar));
        this.camCtr.target.copy(tar);
        this.camCtr.spherical = spherical.makeSafe();
        this.camCtr.enabled=this._oriCamEnabled;
    }

    /**
     * 销毁
     */
    dispose(){
        if(this.requests[0]!==null){
            cancelAnimationFrame(this.requests[0]);
            this.requests[0]=null;
        }
        this.requests=null;
        this.eventCenter=null;
        this._target=null;
        this._camSpeed=null;
        this._minLimit=null;
        this._tV0=null;
        this._tV1=null;
        this._tV2=null;
        this._tS0=null;
        this._oriCamEnabled=null;
        this._linear=null;
    }

    //-----------------------------------私有方法------------------------------------

    init(){
        this.initVariable();
        this.initEventBind();
    }
    initEventBind(){
        this.fireCamGoEvent=(bool)=>{
            this.eventCenter.dispatchEvent({type:'going',paras:{value:bool}});
        };
    }
    initVariable(){
        this.requests=[null];
        this.eventCenter=new EventDispatcher();
        this._target=new Vector3();
        this._camSpeed=DEFAULT_SPEED;
        this._minLimit=DEFAULT_LIMIT;
        this._tV0=new Vector3();
        this._tV1=new Vector3();
        this._tV2=new Vector3();
        this._tS0=new Spherical();
        this._oriCamEnabled=this.camCtr.enabled;
        this._linear=false;
    }
}